package com.itcam.web.manager;

import cn.hutool.core.collection.CollUtil;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.exception.MultiObjectDeleteException;
import com.qcloud.cos.model.*;
import com.itcam.web.config.CosClientConfig;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import com.qcloud.cos.transfer.Download;
import com.qcloud.cos.transfer.TransferManager;
import org.springframework.stereotype.Component;

/**
 * Cos 对象存储操作
 *
 */
@Component
public class CosManager {

    @Resource
    private CosClientConfig cosClientConfig;

    @Resource
    private COSClient cosClient;

    /**
     * 上传对象
     *
     * @param key 唯一键
     * @param localFilePath 本地文件路径
     * @return 上传结果
     */
    public PutObjectResult putObject(String key, String localFilePath) {
        // 创建一个 PutObjectRequest 对象，指定存储桶名称、对象键和本地文件路径
        PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
                new File(localFilePath));
        // 使用 COSClient 上传对象，并返回上传结果
        return cosClient.putObject(putObjectRequest);
    }

    /**
     * 上传对象
     *
     * @param key 唯一键
     * @param file 文件
     * @return 上传结果
     */
    public PutObjectResult putObject(String key, File file) {
        // 创建一个 PutObjectRequest 对象，指定存储桶名称、对象键和文件对象
        PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key,
                file);
        // 使用 COSClient 上传对象，并返回上传结果
        return cosClient.putObject(putObjectRequest);
    }

    /**
     * 下载对象
     *
     * @param key 唯一键
     * @return COSObject 对象
     */
    public COSObject getObject(String key) {
        // 创建一个 GetObjectRequest 对象，指定存储桶名称和对象键
        GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);
        // 使用 COSClient 下载对象，并返回 COSObject 对象
        return cosClient.getObject(getObjectRequest);
    }

    // 复用对象
    private TransferManager transferManager;

    // bean 加载完成后执行
    @PostConstruct
    public void init() {
        // 执行初始化逻辑
        System.out.println("Bean initialized!");
        // 多线程并发上传下载
        ExecutorService threadPool = Executors.newFixedThreadPool(32);
        transferManager = new TransferManager(cosClient, threadPool);
    }

    /**
     * 下载对象到本地文件
     *
     * @param key
     * @param localFilePath
     * @return
     * @throws InterruptedException
     */
    public Download download(String key, String localFilePath) throws InterruptedException {
        File downloadFile = new File(localFilePath);
        GetObjectRequest getObjectRequest = new GetObjectRequest(cosClientConfig.getBucket(), key);
        Download download = transferManager.download(getObjectRequest, downloadFile);
        // 同步等待下载完成
        download.waitForCompletion();
        return download;
    }

    /**
     * 删除对象
     *
     * @param key 要删除对象的唯一键，用于标识存储在对象存储中的特定对象
     * @throws CosClientException 当客户端与对象存储服务通信时发生错误，例如网络问题、认证失败等，抛出此异常
     * @throws CosServiceException 当对象存储服务端出现问题，例如服务不可用、请求参数错误等，抛出此异常
     */
    public void deleteObject(String key) throws CosClientException, CosServiceException {
        // 调用 COSClient 的 deleteObject 方法，传入存储桶名称和对象键，以删除指定对象
        cosClient.deleteObject(cosClientConfig.getBucket(), key);
    }

    /**
     * 批量删除对象
     *
     * @param keyList
     * @return
     * @throws MultiObjectDeleteException
     * @throws CosClientException
     * @throws CosServiceException
     */
    public DeleteObjectsResult deleteObjects(List<String> keyList)
            throws MultiObjectDeleteException, CosClientException, CosServiceException {
        DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(cosClientConfig.getBucket());
        // 设置要删除的key列表, 最多一次删除1000个
        ArrayList<DeleteObjectsRequest.KeyVersion> keyVersions = new ArrayList<>();
        // 传入要删除的文件名
        // 注意文件名不允许以正斜线/或者反斜线\开头，例如：
        // 存储桶目录下有a/b/c.txt文件，如果要删除，只能是 keyList.add(new KeyVersion("a/b/c.txt")), 若使用 keyList.add(new KeyVersion("/a/b/c.txt"))会导致删除不成功
        for (String key : keyList) {
            keyVersions.add(new DeleteObjectsRequest.KeyVersion(key));
        }
        deleteObjectsRequest.setKeys(keyVersions);
        DeleteObjectsResult deleteObjectsResult = cosClient.deleteObjects(deleteObjectsRequest);
        return deleteObjectsResult;
    }

    /**
     * 删除目录
     *
     * @param delPrefix 要删除目录的前缀，用于指定要删除的目录范围
     * @throws CosClientException 当客户端与对象存储服务通信时发生错误，例如网络问题、认证失败等，抛出此异常
     * @throws CosServiceException 当对象存储服务端出现问题，例如服务不可用、请求参数错误等，抛出此异常
     */
    public void deleteDir(String delPrefix) throws CosClientException, CosServiceException {
        // 创建一个 ListObjectsRequest 对象，用于列出存储桶中的对象
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        // 设置要操作的存储桶名称
        listObjectsRequest.setBucketName(cosClientConfig.getBucket());
        // 设置列出对象的前缀，即只列出以 delPrefix 开头的对象
        listObjectsRequest.setPrefix(delPrefix);
        // 设置每次列出对象的最大数量，一次 listobject 最大支持 1000 个
        listObjectsRequest.setMaxKeys(1000);

        // 用于保存每次列出对象的结果
        ObjectListing objectListing;

        do {
            // 调用 COSClient 的 listObjects 方法，列出符合条件的对象
            objectListing = cosClient.listObjects(listObjectsRequest);
            // 获取列出的对象摘要列表
            List<COSObjectSummary> cosObjectSummaries = objectListing.getObjectSummaries();
            // 如果对象摘要列表为空，说明没有符合条件的对象，跳出循环
            if (CollUtil.isEmpty(cosObjectSummaries)) {
                break;
            }

            // 用于保存要删除的对象的键信息
            ArrayList<DeleteObjectsRequest.KeyVersion> delObjects = new ArrayList<>();
            // 遍历对象摘要列表，将每个对象的键添加到 delObjects 列表中
            for (COSObjectSummary cosObjectSummary : cosObjectSummaries) {
                delObjects.add(new DeleteObjectsRequest.KeyVersion(cosObjectSummary.getKey()));
            }

            // 创建一个 DeleteObjectsRequest 对象，用于批量删除对象
            DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(cosClientConfig.getBucket());
            // 设置要删除的对象的键列表
            deleteObjectsRequest.setKeys(delObjects);
            // 调用 COSClient 的 deleteObjects 方法，批量删除对象
            cosClient.deleteObjects(deleteObjectsRequest);

            // 获取下一次列出对象的起始标记
            String nextMarker = objectListing.getNextMarker();
            // 设置下一次列出对象的起始标记
            listObjectsRequest.setMarker(nextMarker);
            // 如果结果被截断，说明还有更多对象需要列出，继续循环
        } while (objectListing.isTruncated());
    }


}

